package com.cxs.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.cxs.constant.AuthConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: CXS
 * 访问时校验token 并且把jwt转成security认识的对象
 */
@Configuration
public class JwtCheckTokenFilter extends OncePerRequestFilter {


    @Autowired
    private StringRedisTemplate redisTemplate;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.addHeader("content-type", "application/json;charset=utf-8");
        // 先拿到请求路径 如果是登录的 就方行 或者走验证码等处理 这里不做演示
        String path = request.getRequestURI();
        String method = request.getMethod();
        if ("/doLogin".equals(path) && "POST".equalsIgnoreCase(method)) {
            // 进入说明是登录的请求 直接方形 或者处理验证码之类的
            filterChain.doFilter(request, response);
            return;
        }
        // 到这里说明不是登录的或者不是方行的路径 需要检查token
        // 我们把token放在header里面 Authorization = bearer xxxxxx
        String authorization = request.getHeader(AuthConstant.AUTHORIZATION);
        if (!StringUtils.isEmpty(authorization)) {
            // 如果有authorization 就分割一下
            String realToken = authorization.replaceAll("bearer ", "");
            if (!StringUtils.isEmpty(realToken) && redisTemplate.hasKey(AuthConstant.AUTH_JWT_PREFIX + realToken)) {
                // 如果有token 并且redis也有 说明没有过期 我们就解析
                DecodedJWT verify = null;
                try {
                    // 解析
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(AuthConstant.AUTH_JWT_KEY_SIGN)).build();
                    verify = jwtVerifier.verify(realToken);
                } catch (Exception e) {
                    // 如果报错了 说明token解析失败 把错误信息写出去
                    response.getWriter().write("token校验失败");
                    return;
                }
                // 拿到名字
                String username = verify.getSubject();
                // 拿到权限
                Claim authsClaim = verify.getClaim("auths");
                List<String> auths = authsClaim.asList(String.class);
                ArrayList<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>(auths.size() * 2);
                for (String auth : auths) {
                    grantedAuthorities.add(new SimpleGrantedAuthority(auth));
                }
                // 变成security认识的对象
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities);
                // 设置给security
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                filterChain.doFilter(request, response);
                return;
            }
        }
        // 到这里直接把失败写出去 可以写出json
        response.getWriter().write("token校验失败");
    }

}
